home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / diskutil / ccache.arc / cache.c next >
C/C++ Source or Header  |  1989-04-05  |  8KB  |  349 lines

  1. /*
  2.  * Floppy Disk Cache (Atari ST)
  3.  *
  4.  * Copyright 1988, Eric Gisin <egisin@UWaterloo.CA>
  5.  * this program may be copied as long as this copyright notice is retained.
  6.  * the author will not be liable for any damages resulting from the use of this program.
  7.  *
  8.  * Usage: cache [drive][size]
  9.  *    can be loaded twice to handle two drives.
  10.  *
  11.  * The cache is based on paging and page replacement techniques
  12.  * used in virtual memory systems. The replacement algorithm is
  13.  * similiar to "not frequently used" (NFU).
  14.  *
  15.  * each sector of the disk has a (struct) Cache element
  16.  * that contains a pointer to a cache buffer,
  17.  * and an "average usage" value.  The average is a combination
  18.  * of long term usage and short term usage (somethink like 4bsd's load avg).
  19.  * there are also averages for total IO, read, write, hits, and reclaims.
  20.  * an average is increased for each reference (read/write),
  21.  * and all averages are decreased (by a percentage) every 100 accesses.
  22.  * the reference function is:    avg += A_INC;    A_INC = 16;
  23.  * the update function is:    avg -= 1/8*avg;
  24.  * for these parameters, the total avg will tend between 12800 and 14400.
  25.  */
  26.  
  27. #include <osbind.h>
  28. #include "cache.h"
  29.  
  30. /* cache.c can compile with DLIBS or MWC */
  31. #ifndef    MWC
  32. #define    DLIBS
  33. #endif
  34.  
  35. #ifdef MJC
  36. #define    ASM    1    /* inline assembler */
  37. #endif
  38.  
  39. #define    reg    register
  40.  
  41. #ifdef OK
  42. typedef    long (*Func)();        /* function returning long */
  43. #else
  44. typedef    char *    Func;        /* good enough */
  45. #endif
  46.  
  47. /* define system variables */
  48. #define    bios_rw    (*(Func*)0x000476)    /* bios read/write routine */
  49. #define    bios_mc    (*(Func*)0x00047E)    /* bios media change routine */
  50.  
  51. #ifdef OK
  52. Func    old_rw;
  53. Func    cur_mc;
  54. #else
  55. long    (*old_rw)();        /* previous rwabs routine */
  56. long    (*cur_mc)();        /* current mediach routine */
  57. #endif
  58.  
  59. /* per-device data */
  60. int    c_dev = 0;        /* cached device, A: */
  61. int    disable;        /* flag to disable */
  62. short    stats [STATS];        /* global averages */
  63. Cache    cache [NSECT];        /* per sector averages and cbufs indices */
  64. short    csize;            /* size of cache in sectors */
  65. Sector*    cbufs;            /* cached sector buffers */
  66. short    cycle;            /* count from 100 to 0 */
  67. short    nfree;            /* next free cache sector */
  68.  
  69. #ifdef DLIBS
  70.  
  71. extern char *_base, *_break;    /* program basepage, heap break */
  72. long    _STKSIZ = -2L;        /* grab all free memory for heap */
  73.  
  74. #else    /* MWC */
  75.  
  76. #include <basepage.h>
  77. #define    _base    ((char *)BP)
  78. #define    _break    _stack        /* MWC combines _STKSIZ and _break as _stack */
  79. char   *_break = (char *)300*1024L; /* grab 300K for heap */
  80.  
  81. #endif
  82.  
  83. char    header [] = "ST Floppy Disk Cache (by Eric Gisin)\r\n";
  84.  
  85. /*
  86.  * setup bios disk IO routines, terminate and stay resident.
  87.  */
  88. main(argc, argv)
  89.     int argc;
  90.     char **argv;
  91. {
  92.     long stack;
  93.     extern long cache_rw();
  94.     extern char *sbrk(/*size_t n*/);
  95.  
  96.     Cconws(header);
  97.  
  98.     if (argc > 1) {
  99.         char *arg = argv[1];
  100.         if ('a' <= *arg && *arg <= 'z')
  101.             c_dev = *arg++ - 'a';
  102.         if ('A' <= *arg && *arg <= 'Z')
  103.             c_dev = *arg++ - 'A';
  104.         csize = atoi(arg) * 1024/sizeof(Sector);
  105.     }
  106.     if (csize < 100)
  107.         csize = 100;        /* 50K is minimum */
  108.  
  109. #ifndef DEBUG
  110.     if (Rwabs(CSTAT, (char*)NULL, 0, 0, c_dev) != NULL) { /* already loaded */
  111.         Cconws("Cache already loaded\r\n");
  112.         return 0;
  113.     }
  114. #endif
  115.  
  116. #if 0
  117.     cbufs = (Sector*) sbrk(csize * sizeof(Sector));
  118. #else                /* avoid loading long multiply */
  119.     cbufs = (Sector*) sbrk((size_t)&((Sector*)NULL)[csize]);
  120. #endif
  121.     if (cbufs == NULL) {
  122.         Cconws("Not enough memory\r\n");
  123.         Pterm(-1);
  124.     }
  125.  
  126.     /* set up our routines for bios rwabs */
  127.     stack = Super(NULL);        /* super mode */
  128.     old_rw = bios_rw;
  129.     bios_rw = cache_rw;
  130.     cur_mc = bios_mc;
  131.     Super(stack);            /* user mode */
  132.  
  133.     cache_init();
  134.  
  135.     Ptermres(_break - _base, 0);    /* terminate, stay resident */
  136. }
  137.  
  138. /* clear the cache */
  139. cache_init()
  140. {
  141.   reg    int    i;
  142.  
  143.     cycle = CYCLE;
  144.     nfree = 0;
  145.     for (i = 0; i < STATS; i ++)
  146.         stats[i] = 0;
  147.     for (i = 0; i < NSECT; i ++) {
  148.         cache[i].avg = 0;
  149.         cache[i].buf = NOBUF;
  150.     }
  151.     return 0;
  152. }
  153.  
  154. /* our BIOS rwabs routine */
  155. long
  156. cache_rw(func, addr, count, sect, dev)
  157.     int    func;        /* IO function */
  158.     Sector*    addr;        /* buffer address */
  159.     int    count;        /* sector count */
  160.     int    sect;        /* sector number */
  161.     int    dev;        /* disk drive */
  162. {
  163.   reg    int    s;
  164.   reg    int    i;
  165.     int    n;
  166.     long    l;
  167.  
  168.     if (dev != c_dev || disable)
  169.         return (*old_rw)(func, addr, count, sect, dev);
  170.  
  171.     /* recognize magic function for cstat */
  172.     if (func == CSTAT)
  173.         return (sect==0) ? stats : cache;
  174.  
  175.     if (func == CINIT)
  176.         return cache_init();
  177.  
  178.     /* check for media change */
  179.     /* floppy rwabs will return -14 if real media change */
  180.     if ((*cur_mc)(dev) != 0) {
  181.         l = (*old_rw)(0, 0xFC0000, 0, 0, dev);    /* dummy read */
  182.         if (l != 0) {
  183.             cache_init();
  184.             return l;    /* always -14 (media changed)? */
  185.         }
  186.     }
  187.  
  188.     if (sect < 0 || count < 0 || sect+count < 0)
  189.         return -5;        /* sector out of range */
  190.  
  191.     /* update cache averages */
  192.     for (i = 0; i < count; i ++) {
  193.         s = sect + i;
  194.         stats[TOTAL] += A_INC;
  195.         stats[((func&1)==0) ? READS : WRITES] += A_INC;
  196.         if (s < NSECT)
  197.             cache[s].avg += A_INC;
  198.         if (-- cycle < 0) {
  199.             cycle = CYCLE;
  200.             update_averages();
  201.         }
  202.     }
  203.  
  204.     /* copy cache to read buffer */
  205.     if ((func&1)==0 && sect>0) {
  206.         for (i = 0; i < count; i ++) {
  207.         s = sect + i;
  208.         n = cache[s].buf;
  209.         if (n == NOBUF)
  210.             break;
  211.         copy_sector(&cbufs[n], addr++);
  212.         stats[HITS] += A_INC;
  213.         }
  214.         sect += i;    count -= i;
  215.     }
  216.  
  217.     /* do BIOS IO */
  218.     l = (*old_rw)(func, addr, count, sect, dev);
  219.     if (l != 0)
  220.         return l;
  221.  
  222.     /* update cache from IO buffer */
  223.     for (i = 0; i < count; i ++) {
  224.         s = sect + i;
  225.         n = cache[s].buf;
  226.         if (n == NOBUF)
  227.             get_free(s);
  228.         n = cache[s].buf;
  229.         if (n != NOBUF)
  230.             copy_sector(addr + i, &cbufs[n]);
  231.     }
  232.  
  233.     return 0;
  234. }
  235.  
  236. /*
  237.  * update averages.
  238.  * this is called once per N accesses.
  239.  */
  240. update_averages()
  241. {
  242.   reg    int    i;
  243.     int    n;
  244.  
  245.     for (i = 0; i < STATS; ++ i) {
  246.         stats[i] -= stats[i]+7 >> 3;
  247.     }
  248.  
  249.     for (i = 0; i < NSECT; ++ i) {
  250.         cache[i].avg -= cache[i].avg+7 >> 3;
  251.         n = cache[i].buf;
  252.         if (!(n == NOBUF || 0<=n && n<csize)) {
  253.             disable = 1;
  254.             Cconws("<cache: internal error>");
  255.         }
  256.     }
  257. }
  258.  
  259. /*
  260.  * get cache block with lowest avg less than requested block.
  261.  * somewhat inefficient, but should not be called often.
  262.  */
  263. get_free(c)
  264.     int    c;            /* sector */
  265. {
  266.   reg    int    s;
  267.   reg    Cache *    p;
  268.   reg    Cache *    m;
  269.  
  270.     if (nfree < csize) {
  271.         cache[c].buf = nfree++;
  272.         return;
  273.     }
  274.  
  275.     /* find sector with a buffer and lowest avg */
  276.     p = &cache[0];
  277.     m = &cache[c];
  278.     for (s = 0; s < NSECT; s ++, p ++)
  279.         if (p->buf != NOBUF && p->avg <= m->avg)
  280.             m = p;
  281.  
  282.     /* if that sector has lower avg than requested sector, reclaim */
  283.     if (m->avg < cache[c].avg) {
  284.         cache[c].buf = m->buf;
  285.         m->buf = NOBUF;
  286.         stats[RECLMS] += A_INC;
  287.     }
  288. }
  289.  
  290. copy_sector(s, d)
  291.     Sector *s;
  292.     Sector *d;
  293. {
  294.     int    i;
  295.   reg    char   *dp = d->data;    /* A5 in MJC */
  296.   reg    char   *sp = s->data;    /* A4 in MJC */
  297.  
  298.     if ((s&1) | (d&1)) {    /* odd address! */
  299.         i = sizeof(Sector);
  300.         while (--i >= 0)
  301.             *dp++ = *sp++;
  302.     } else {
  303. #ifndef    ASM
  304.         /* usually generates loop: move.w (A0)+, (A1)+; dbra D0, loop */
  305.         *d = *s;
  306. #else
  307.         /* this is twice as fast as above */
  308.         sizeof(Sector)/sizeof(long)/4 - 1;    /* result to D0 */
  309.         /* isn't machine language fun!      loop: */
  310.         asm(= 10972);            /* move.l (A4)+, (A5)+ */
  311.         asm(= 10972);            /* move.l (A4)+, (A5)+ */
  312.         asm(= 10972);            /* move.l (A4)+, (A5)+ */
  313.         asm(= 10972);            /* move.l (A4)+, (A5)+ */
  314.         asm(= 20936 = -10);        /* dbra D0, loop */
  315. #endif
  316.     }
  317. }
  318.  
  319. /* allocate n bytes from heap (stack segment). NULL on error */
  320. char *sbrk(n)
  321.     size_t n;
  322. {
  323.     extern char *_break;        /* current heap break */
  324.     char *p = _break;
  325.     int dummy;            /* &dummy is almost SP */
  326.  
  327.     _break += n;
  328.     if (_break+256 > (char*)&dummy) {
  329.         _break = p;
  330.         return NULL;
  331.     }
  332.     return p;
  333. }
  334.  
  335. #if 0
  336. int brk(p)
  337.     char *p;            /* new break address */
  338. {
  339.     int dummy;            /* &dummy is almost SP */
  340.     extern char *_break;        /* current break address */
  341.     extern char *end;        /* initial _break; */
  342.  
  343.     if (p < end || p+256 > (char *)&dummy)
  344.         return -1;
  345.     _break = p;
  346.     return 0;
  347. }
  348. #endif
  349.